package log

import (
	"errors"
	"io"
	"os"
	"sync"
	"sync/atomic"
)

var (
	ErrNoSpace = errors.New("no space")
)

type BytesReader interface {
	ReadBytes(n int, cb func([]byte)) (int, error)
}

type ByteBuffer []byte

func (b ByteBuffer) ReadBytes(n int, cb func([]byte)) (int, error) {
	if n > len(b) {
		n = len(b)
	}
	cb(b[:n])
	return n, nil
}

// 缓存器
type RingBuffer struct {
	buf  []byte //缓冲区间
	wpos int32  //写位置
	rpos int32  //可写位置
	size int32
	once sync.Once // Protects closing done
	done chan struct{}
}

func NewRingBuffer(size int) *RingBuffer {
	if size < 1024*1024 {
		size = 1024 * 1024
	}
	if size > 0x40000000 { //1G
		size = 0x40000000
	}
	b := &RingBuffer{
		buf:  make([]byte, size),
		done: make(chan struct{}),
	}
	return b
}

// 当前数据长度
func (b *RingBuffer) Len() int {
	return int(atomic.LoadInt32(&b.size))
}

func (b *RingBuffer) GetPos() int {
	return int(atomic.LoadInt32(&b.wpos))
}

// 回退写
func (b *RingBuffer) BackWrite(size int) {
	atomic.AddInt32(&b.wpos, -int32(size))
}

// 写数据，要么全部写成功，要么不写
func (b *RingBuffer) Write(p []byte) (int, error) {
	return b.TryWrite(p)
}

func (b *RingBuffer) TryWrite(p []byte) (int, error) {
	select {
	case <-b.done:
		return 0, os.ErrClosed
	default:
	}
	size := len(p)
	if size == 0 {
		return 0, nil
	}
	nr := 0
	wpos := int(atomic.LoadInt32(&b.wpos))
	rpos := int(atomic.LoadInt32(&b.rpos))
	c := atomic.LoadInt32(&b.size)
	if wpos > rpos || c == 0 {
		//可写位置[wpos,len(buf))+[0,rpos)
		nr = copy(b.buf[wpos:], p)
		wpos += nr
		if wpos == len(b.buf) {
			wpos = 0
		}
		if nr < size {
			r := copy(b.buf[:rpos], p[nr:])
			nr += r
			wpos += r
		}
	} else if rpos-wpos > 0 {
		//可写位置[wpos,rpos)
		nr = copy(b.buf[wpos:rpos], p)
		wpos += nr
	}
	if nr == size { //size已经改变，这里使用原始的长度对比
		atomic.StoreInt32(&b.wpos, int32(wpos))
		atomic.AddInt32(&b.size, int32(nr))
		return nr, nil
	}
	return 0, ErrNoSpace
}

// 读数据
func (b *RingBuffer) Read(buf []byte) (int, error) {
	size := len(buf)
	if size == 0 {
		return 0, nil
	}
	return b.ReadBytes(size, func(p []byte) {
		copy(buf, p)
	})
}

func (b *RingBuffer) ReadBytes(n int, cb func([]byte)) (int, error) {
	return b.TryReadBytes(n, cb)
}

func (b *RingBuffer) TryReadBytes(n int, cb func([]byte)) (int, error) {
	if n <= 0 {
		return 0, nil
	}
	rpos := int(atomic.LoadInt32(&b.rpos))
	wpos := int(atomic.LoadInt32(&b.wpos))
	c := atomic.LoadInt32(&b.size)
	nr := 0
	if rpos < wpos {
		//可读位置[rpos,wpos)
		if n > wpos-rpos {
			n = wpos - rpos
		}
		cb(b.buf[rpos : rpos+n])
		rpos += n
		nr = n
	} else if rpos > wpos || c > 0 {
		//可读位置[rpos,len(buf))+[0,wpos)
		nr = len(b.buf) - rpos
		if nr > n {
			nr = n
		}
		cb(b.buf[rpos : rpos+nr])
		rpos += nr
		if nr < n {
			n -= nr
			if n > wpos {
				n = wpos
			}
			cb(b.buf[:n])
			nr += n
			rpos = n
		} else if rpos == len(b.buf) {
			rpos = 0
		}
	}
	if nr > 0 {
		atomic.StoreInt32(&b.rpos, int32(rpos))
		atomic.AddInt32(&b.size, -int32(nr))
	} else {
		select {
		case <-b.done:
			return 0, io.EOF
		default:
		}
	}
	return nr, nil
}

func (b *RingBuffer) Close() error {
	select {
	case <-b.done:
		return os.ErrClosed
	default:
		b.once.Do(func() { close(b.done) })
	}
	return nil
}
